package com.demo.seckill.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.aop.Servicelock;
import com.demo.config.DSKey;
import com.demo.seckill.entity.Seckill;
import com.demo.seckill.entity.SuccessKilled;
import com.demo.seckill.enums.SeckillStatEnum;
import com.demo.seckill.mapper.SeckillMapper;
import com.demo.seckill.service.ISeckillService;
import com.demo.seckill.service.ISuccessKilledService;
import com.demo.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 秒杀库存表 服务实现类
 * </p>
 *
 * @author 翌擎科技
 * @since 2021-10-26
 */
@Service
@DS(DSKey.SECKILL)
@Slf4j
public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> implements ISeckillService {

    @Autowired
    private ISuccessKilledService successKilledService;
    @Autowired
    private RedisUtil redisUtil;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SeckillStatEnum startOneSeckill(long seckillId, long userId) {
        //查询库存
        Seckill seckill = this.getOne(new LambdaQueryWrapper<Seckill>()
                .eq(Seckill::getSeckillId, seckillId));

        if (seckill.getNumber() > 0) {
            /**
             * 未加锁，导致库存数量不一致
             */
            //扣减库存
            this.update(new LambdaUpdateWrapper<Seckill>()
                    .set(Seckill::getNumber, seckill.getNumber() - 1)
                    .eq(Seckill::getSeckillId, seckillId));

            //创建订单
            SuccessKilled successKilled = SuccessKilled.builder()
                    .seckillId(seckillId)
                    .userId(userId)
                    .state(0)
                    .createTime(LocalDateTime.now())
                    .build();
            successKilledService.save(successKilled);
            //支付
            return SeckillStatEnum.SUCCESS;
        } else {
            return SeckillStatEnum.END;
        }
    }

    /**
     * 默认为非公平锁，true为公平锁
     */
    private Lock lock = new ReentrantLock(true);

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SeckillStatEnum startTwoSeckill(long seckillId, long userId) {
        //加锁
        lock.lock();
        try {

            //查询库存
            Seckill seckill = this.getOne(new LambdaQueryWrapper<Seckill>()
                    .eq(Seckill::getSeckillId, seckillId));

            if (seckill.getNumber() > 0) {
                /**
                 * 未加锁，导致库存数量不一致
                 */
                baseMapper.deductionNumber(seckillId);

                //创建订单
                SuccessKilled successKilled = SuccessKilled.builder()
                        .seckillId(seckillId)
                        .userId(userId)
                        .state(0)
                        .createTime(LocalDateTime.now())
                        .build();
                successKilledService.save(successKilled);
                //支付
                return SeckillStatEnum.SUCCESS;
            } else {
                return SeckillStatEnum.END;
            }
        } catch (Exception e) {
            log.info("服务器异常");
            e.printStackTrace();
        } finally {
            //释放锁
            lock.unlock();
        }
        return SeckillStatEnum.SUCCESS;
    }

    @Servicelock
    @Transactional(rollbackFor = Exception.class)
    @Override
    public SeckillStatEnum startThreeSeckill(long seckillId, long userId) {
        //查询库存
        Seckill seckill = this.getOne(new LambdaQueryWrapper<Seckill>()
                .eq(Seckill::getSeckillId, seckillId));

        if (seckill.getNumber() > 0) {
            baseMapper.deductionNumber(seckillId);
            //创建订单
            SuccessKilled successKilled = SuccessKilled.builder()
                    .seckillId(seckillId)
                    .userId(userId)
                    .state(0)
                    .createTime(LocalDateTime.now())
                    .build();
            successKilledService.save(successKilled);
            //支付
            return SeckillStatEnum.SUCCESS;
        } else {
            return SeckillStatEnum.END;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SeckillStatEnum startFourSeckill(long seckillId, long userId) {
        //查询库存 加悲观锁
        int number = baseMapper.queryNumber(seckillId);
        if (number > 0) {
            //扣减库存
            baseMapper.deductionNumber(seckillId);
            //创建订单
            SuccessKilled successKilled = SuccessKilled.builder()
                    .seckillId(seckillId)
                    .userId(userId)
                    .state(0)
                    .createTime(LocalDateTime.now())
                    .build();
            successKilledService.save(successKilled);
            //支付
            return SeckillStatEnum.SUCCESS;
        } else {
            return SeckillStatEnum.END;
        }
    }

    @Override
    public SeckillStatEnum startFiveSeckill(long seckillId, long userId) {
        //查询库存
        Seckill seckill = this.getOne(new LambdaQueryWrapper<Seckill>()
                .eq(Seckill::getSeckillId, seckillId));

        if (seckill.getNumber() > 0) {
            //乐观锁
            baseMapper.deductionNumberOcc(seckillId, seckill.getVersion());
            //创建订单
            SuccessKilled successKilled = SuccessKilled.builder()
                    .seckillId(seckillId)
                    .userId(userId)
                    .state(0)
                    .createTime(LocalDateTime.now())
                    .build();
            successKilledService.save(successKilled);
            //支付
            return SeckillStatEnum.SUCCESS;
        } else {
            return SeckillStatEnum.END;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SeckillStatEnum startDistributedOneSeckill(long seckillId, long userId) {
        boolean lock = false;
        try {
            lock = redisUtil.tryLock(seckillId + "", 3, 20, TimeUnit.SECONDS);
            if (lock) {
                //查询库存
                Seckill seckill = this.getOne(new LambdaQueryWrapper<Seckill>()
                        .eq(Seckill::getSeckillId, seckillId));
                if (seckill.getNumber() > 0) {
                    //扣减库存
                    this.update(new LambdaUpdateWrapper<Seckill>()
                            .set(Seckill::getNumber, seckill.getNumber() - 1)
                            .eq(Seckill::getSeckillId, seckillId));
                    //创建订单
                    SuccessKilled successKilled = SuccessKilled.builder()
                            .seckillId(seckillId)
                            .userId(userId)
                            .state(0)
                            .createTime(LocalDateTime.now())
                            .build();
                    successKilledService.save(successKilled);
                    //支付
                    return SeckillStatEnum.SUCCESS;
                } else {
                    return SeckillStatEnum.END;
                }
            } else {
                return SeckillStatEnum.MUCH;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放锁
            if (lock){
                redisUtil.unLock(seckillId+"");
            }
        }
        return SeckillStatEnum.SUCCESS;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SeckillStatEnum startDistributedTwoSeckill(long seckillId, long userId) {
        boolean lock = false;
        try {
            lock = redisUtil.tryLock(seckillId + "_lock", 3, 20, TimeUnit.SECONDS);
            if (lock) {
                //查询库存
                Seckill seckill = this.getOne(new LambdaQueryWrapper<Seckill>()
                        .eq(Seckill::getSeckillId, seckillId));
                if (seckill.getNumber() > 0) {
                    //扣减库存
                    this.update(new LambdaUpdateWrapper<Seckill>()
                            .set(Seckill::getNumber, seckill.getNumber() - 1)
                            .eq(Seckill::getSeckillId, seckillId));
                    //创建订单
                    SuccessKilled successKilled = SuccessKilled.builder()
                            .seckillId(seckillId)
                            .userId(userId)
                            .state(0)
                            .createTime(LocalDateTime.now())
                            .build();
                    successKilledService.save(successKilled);
                    //支付
                    return SeckillStatEnum.SUCCESS;
                } else {
                    return SeckillStatEnum.END;
                }
            } else {
                return SeckillStatEnum.MUCH;
            }
        } catch (Exception e) {
            //rdis 库存+1
            redisUtil.incr(seckillId + "_num",1);
            e.printStackTrace();
        } finally {
            //释放锁
            if (lock){
                redisUtil.unLock(seckillId+"");
            }
        }
        return SeckillStatEnum.SUCCESS;
    }
}
